<template>
  <div ref="containerEl" class="relative rounded-md border border-solid border-color-border overflow-hidden">
    <div>
      <canvas ref="canvasEl" class="block" :height="height"></canvas>
    </div>
    <div class="absolute right-1 bottom-1" @mousedown.stop @touchstart.stop>
      <a class="text-color-secondary inline-block p-2.5" href="javascript:void(0)" @click="clear">清除</a>
      <a class="text-color-active inline-block p-2.5" href="javascript:void(0)" @click="save">保存</a>
      <a
        class="inline-block p-2.5"
        href="javascript:void(0)"
        :class="[previewSrc ? 'text-color-active' : 'text-color-disabled cursor-not-allowed hover:text-color-disabled']"
        @click="preview"
      >
        预览
      </a>
    </div>
    <div class="hidden">
      <a-image
        :width="200"
        :style="{ display: 'none' }"
        :preview="{
          visible: previewVisible,
          onVisibleChange: setPreviewVisible,
        }"
        :src="previewSrc"
      />
    </div>
    <div
      v-show="loading"
      class="absolute inset-0 bg-black bg-opacity-5 flex items-center justify-center"
      @mousedown.stop
      @touchstart.stop
    >
      <a-spin></a-spin>
    </div>
  </div>
</template>
<script setup lang="ts">
import { message } from "ant-design-vue";
import { off } from "process";
import { onMounted, ref, shallowRef, watch } from "vue";
import { isMobile } from "/@/utils/common";
import { handleEvent, getElRelativeXY } from "/@/utils/dom";
import env from "/@/base/env";
import { Api, uploadReq } from "/@/api/common";
import { BizType, StorageType } from "/@/types/components";

const props = withDefaults(
  defineProps<{
    height: number;
    value: string;
  }>(),
  {
    value: "",
    height: 300,
  }
);
const emits = defineEmits(["change"]);

const containerEl = shallowRef<HTMLDivElement>();
const canvasEl = shallowRef<HTMLCanvasElement>();
function preHandler(e: MouseEvent | TouchEvent) {
  e.preventDefault();
}
function beforeSignStart(e: MouseEvent | TouchEvent) {
  signStart(e);
  if (isMobile) {
    document.addEventListener("touchstart", preHandler, false);
    document.addEventListener("touchmove", signMove, { passive: false });
    document.addEventListener("touchend", beforeSignEnd);
    document.addEventListener("touchcancel", beforeSignEnd);
  } else {
    document.addEventListener("mousemove", signMove);
    document.addEventListener("mouseup", beforeSignEnd);
  }
}
function beforeSignEnd(e: MouseEvent | TouchEvent) {
  signEnd(e);
  if (isMobile) {
    document.removeEventListener("touchstart", preHandler, false);
    document.removeEventListener("touchmove", signMove);
    document.removeEventListener("touchend", beforeSignEnd);
    document.removeEventListener("touchcancel", beforeSignEnd);
  } else {
    document.removeEventListener("mousemove", signMove);
    document.removeEventListener("mouseup", beforeSignEnd);
  }
}

let startData: {
  canvasX: number;
  canvasY: number;
} | null = null;
function signStart(e: MouseEvent | TouchEvent) {
  if (ctx) {
    window.getSelection()?.removeAllRanges();
    document.body.style.userSelect = "none";
    const offset = getElRelativeXY(canvasEl.value as HTMLCanvasElement);
    previewSrc.value = "";
    startData = {
      canvasX: offset.x,
      canvasY: offset.y,
    };
    const info = handleEvent(e);
    ctx.beginPath();
    ctx.moveTo(info.x - offset.x, info.y - offset.y);
  }
}
function signMove(e: MouseEvent | TouchEvent) {
  if (ctx && startData) {
    const info = handleEvent(e);
    ctx.lineTo(info.x - startData.canvasX, info.y - startData.canvasY);
    ctx.stroke();
  }
}
function signEnd(e: MouseEvent | TouchEvent) {
  if (ctx) {
    ctx.closePath();
    document.body.style.userSelect = "text";
  }
  startData = null;
}
let ctx: CanvasRenderingContext2D | null = null;
onMounted(() => {
  if (canvasEl.value && containerEl.value) {
    ctx = canvasEl.value.getContext("2d") as CanvasRenderingContext2D;
    canvasEl.value.width = (canvasEl.value.parentNode as HTMLDivElement).offsetWidth;
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, canvasEl.value.width, canvasEl.value.height);
    ctx.strokeStyle = "#000000";
    ctx.lineWidth = 3;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.shadowBlur = 1;
    ctx.shadowColor = "#000000";
    if (isMobile) {
      containerEl.value.addEventListener("touchstart", beforeSignStart);
    } else {
      containerEl.value.addEventListener("mousedown", beforeSignStart);
    }
  }
});
function clear() {
  if (ctx && canvasEl.value) {
    ctx.clearRect(0, 0, canvasEl.value.width, canvasEl.value.height);
    ctx.fillRect(0, 0, canvasEl.value.width, canvasEl.value.height);
    previewSrc.value = "";
  }
}
function base64ToFile(urlData) {
  let arr = urlData.split(",");
  let mime = arr[0].match(/:(.*?);/)[1];
  let bytes = atob(arr[1]);
  let n = bytes.length;
  let ia = new Uint8Array(n);
  while (n--) {
    ia[n] = bytes.charCodeAt(n);
  }
  return new File([ia], "signImg.png", { type: mime });
}

const loading = ref(false);
function save() {
  if (canvasEl.value) {
    const canvasElement = canvasEl.value;
    const dataUrl = canvasEl.value.toDataURL("image/png");
    const tempImg = new Image();
    tempImg.src = dataUrl;
    tempImg.onload = () => {
      if (!ctx) return;
      let imgData = ctx.getImageData(0, 0, canvasElement.width, canvasElement.height).data;
      let lOffset = canvasElement.width,
        rOffset = 0,
        tOffset = canvasElement.height,
        bOffset = 0;
      let found = false;
      for (let i = 0; i < canvasElement.width; i++) {
        for (let j = 0; j < canvasElement.height; j++) {
          let pos = (i + canvasElement.width * j) * 4;
          if (imgData[pos] == 0 && imgData[pos + 1] == 0 && imgData[pos + 2] == 0 && imgData[pos + 3] == 255) {
            found = true;
            bOffset = Math.max(j, bOffset); // 找到有色彩的最下端
            rOffset = Math.max(i, rOffset); // 找到有色彩的最右端
            tOffset = Math.min(j, tOffset); // 找到有色彩的最上端
            lOffset = Math.min(i, lOffset); // 找到有色彩的最左端
          }
        }
      }
      if (!found) {
        message.info("请签名");
        return;
      }
      lOffset++;
      rOffset++;
      tOffset++;
      bOffset++;
      //周围留白
      const padding = 10;
      const tempCanvas = document.createElement("canvas");
      tempCanvas.width = rOffset - lOffset + padding * 2;
      tempCanvas.height = bOffset - tOffset + padding * 2;
      if (tempCanvas.width < 50 || tempCanvas.height < 50) {
        message.info("请正确签名");
        return;
      }
      const tempCtx = tempCanvas.getContext("2d") as CanvasRenderingContext2D;
      tempCtx.drawImage(
        tempImg,
        lOffset - padding,
        tOffset - padding,
        tempCanvas.width,
        tempCanvas.height,
        0,
        0,
        tempCanvas.width,
        tempCanvas.height
      );
      // previewSrc.value = tempCanvas.toDataURL("image/png");
      saveSignImg(tempCanvas.toDataURL("image/png"));
    };
  }
}
async function saveSignImg(base64Str: string) {
  const file = base64ToFile(base64Str);
  const formData = new FormData();
  formData.append("file", file);
  formData.append("bizType", BizType.iot);
  formData.append("storageType", StorageType.minio);
  let url = env.baseUrl + Api.upload;
  loading.value = true;
  let req = await uploadReq(url, formData);
  loading.value = false;
  if (req) {
    message.success("保存成功");
    previewSrc.value = req.url;
    emits("change", req.url);
  }
}

const previewSrc = ref(props.value);
watch(
  () => props.value,
  (v) => {
    previewSrc.value = v;
  }
);
const previewVisible = ref(false);
function setPreviewVisible(v) {
  previewVisible.value = v;
}
function preview() {
  if (!previewSrc.value) return;
  previewVisible.value = true;
}
</script>
